package com.flowable.sample.holiday;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;

public class SampleHolidayRequestMain {

  private final static Scanner scanner= new Scanner(System.in);

  public static void main(String[] args) {
    // 1. 数据源配置，初始化流程引擎
    ProcessEngine processEngine = _01initCfg();

    // 2. 部署流程文件
    Deployment deployment = _02deployProcessFile(processEngine);

    // 3. 查询已知的流程定义
    ProcessDefinition processDefinition = _03getProcessDefinition(processEngine, deployment.getId());
    System.out.println("Found process definition : " + processDefinition.getName());

    // 4. 启动流程实例
    ProcessInstance processInstance = _04startProcessInstance(processEngine);
    // 5. 查询并完成任务
    _05completeTask(processEngine, 1);

    /**
     * 6. 被批准、拒绝时执行的自动逻辑
     *   6.1 定义 JavaDelegate 子类 com.flowable.holidayrequest.sample.CallExternalSystemDelegate
     *   6.2 xml 配置被批准时自动执行逻辑  <serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="com.flowable.holidayrequest.sample.CallExternalSystemDelegate"/>
     */

    // 7. 查询历史
    _07getHistoryData(processEngine, processInstance.getId());
  }

  /**
   * 1. 数据源配置，初始化流程引擎
   * @return
   */
  public static ProcessEngine _01initCfg(){
    ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
        .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1")
        .setJdbcUsername("sa")
        .setJdbcPassword("")
        .setJdbcDriver("org.h2.Driver")
        .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

    ProcessEngine processEngine = cfg.buildProcessEngine();
    return processEngine;
  }

  /**
   * 2. 部署流程文件
   * @param processEngine
   * @return
   */
  public static Deployment _02deployProcessFile(ProcessEngine processEngine){
    RepositoryService repositoryService = processEngine.getRepositoryService();
    Deployment deployment = repositoryService.createDeployment()
        .addClasspathResource("holiday-request.bpmn20.xml")
//        .addClasspathResource("My_Holiday_Request.bpmn20.xml")
        .deploy();
    return deployment;
  }

  /**
   * 3. 查询已知的流程定义
   * @param processEngine
   * @param deploymentId
   * @return
   */
  public static ProcessDefinition _03getProcessDefinition(ProcessEngine processEngine, String deploymentId){
    RepositoryService repositoryService = processEngine.getRepositoryService();
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .deploymentId(deploymentId)
        .singleResult();
    return processDefinition;
  }

  /**
   * 4. 启动流程实例、注意发起的变量是没有发起人的，只有发起的一些配置参数，发起的 candidateGroups 是 XML 流程文件配置好的
   * @param processEngine
   * @return
   */
  public static ProcessInstance _04startProcessInstance(ProcessEngine processEngine){
    RuntimeService runtimeService = processEngine.getRuntimeService();
    Map<String, Object> variables = prepareStartProcessVariables(false);

    // 注意传入 Key 的是流程 XML process 节点中的 ID
    return runtimeService.startProcessInstanceByKey("holidayRequest", variables);
  }

  /**
   * 准备启动流程实例的数据
   * @param scanInput
   * @return
   */
  public static Map<String, Object> prepareStartProcessVariables(boolean scanInput){
    Map<String, Object> variables = new HashMap<>();
    if(!scanInput){
      variables.put("employee", "zhangsan");
      variables.put("nrOfHolidays", 10);
      variables.put("description", "我要请假！");
    } else {
      System.out.println("Who are you?");
      String employee = scanner.nextLine();

      System.out.println("How many holidays do you want to request?");
      Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

      System.out.println("Why do you need them?");
      String description = scanner.nextLine();

      variables.put("employee", employee);
      variables.put("nrOfHolidays", nrOfHolidays);
      variables.put("description", description);
    }

    return variables;
  }

  /**
   * 5. 查询并完成任务
   * @param processEngine
   * @param taskIndexParam
   */
  public static void _05completeTask(ProcessEngine processEngine, Integer taskIndexParam){
    TaskService taskService = processEngine.getTaskService();
    // 注意 candidateGroups 是静态值， <userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
    System.out.println("You have " + tasks.size() + " tasks:");
    for (int i=0; i<tasks.size(); i++) {
      System.out.println((i + 1) + ") " + tasks.get(i).getName());
    }

    System.out.println("Which task would you like to complete?");
    int taskIndex = Objects.nonNull(taskIndexParam) ? taskIndexParam : Integer.valueOf(scanner.nextLine());
    Task task = tasks.get(taskIndex - 1);
    Map<String, Object> processVariables = taskService.getVariables(task.getId());

    // 注意受让人是 employee 是动态值， <userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
    System.out.println(processVariables.get("employee") + " wants " + processVariables.get("nrOfHolidays") + " of holidays. Do you approve this(y/n)?");

    boolean approved = scanner.nextLine().toLowerCase().equals("y");
    Map<String, Object> variables = new HashMap<>();
    variables.put("approved", approved);
    taskService.complete(task.getId(), variables);
  }

  /**
   * 7. 查询历史
   * @param processEngine
   */
  public static final void _07getHistoryData(ProcessEngine processEngine, String processInstanceId){
    HistoryService historyService = processEngine.getHistoryService();
    List<HistoricActivityInstance> activities =
        historyService.createHistoricActivityInstanceQuery()
            .processInstanceId(processInstanceId)
            .finished()
            .orderByHistoricActivityInstanceEndTime().asc()
            .list();

    for (HistoricActivityInstance activity : activities) {
      System.out.println(activity.getActivityId() + " took " + activity.getDurationInMillis() + " milliseconds");
    }
  }

}
